package cn.bugstack.mybatis.builder.xml;

import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sql.DataSource;

import cn.bugstack.mybatis.builder.BaseBuilder;
import cn.bugstack.mybatis.datasource.DataSourceFactory;
import cn.bugstack.mybatis.io.Resources;
import cn.bugstack.mybatis.mapping.BoundSql;
import cn.bugstack.mybatis.mapping.Enviornment;
import cn.bugstack.mybatis.mapping.MappedStatement;
import cn.bugstack.mybatis.mapping.MappedStatement.Builder;
import cn.bugstack.mybatis.mapping.SqlCommandType;
import cn.bugstack.mybatis.session.Configuration;
import cn.bugstack.mybatis.transaction.TransactionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

/**
 * description: xml配置构建器
 * taskId：
 * <pre>
 * change history:
 * date             defect#             person                comments
 * ---------------------------------------------------------------------
 * 2024/11/25       ********         zhaochaoyue        create file
 * </pre>
 *
 * @author: zhaochaoyue
 * @date: 2024/11/25 16:53
 * @copyright: 2024
 */
public class XMLConfigBuilder extends BaseBuilder {
    
    private Element root;

    public XMLConfigBuilder(Reader reader) {
        //调用父类初始化Configuration
        super(new Configuration());
        //dom4j处理 xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(new InputSource(reader));
            root = document.getRootElement();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
    
    //解析配置 类型别名 插件 对象工厂 对象包装工厂 设置环境 类型转换 映射器
    public Configuration parse(){
        try {
            //解析环境
            environmentsElement(root.element("environments"));
            //映射解析器
            mapperElement(root.element("mappers"));
        } catch (Exception e) {
            throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: "+e,e);
        }
        return configuration;
    }

    /**
     * <environments default="development">
     * <environment id="development">
     * <transactionManager type="JDBC">
     * <property name="..." value="..."/>
     * </transactionManager>
     * <dataSource type="POOLED">
     * <property name="driver" value="${driver}"/>
     * <property name="url" value="${url}"/>
     * <property name="username" value="${username}"/>
     * <property name="password" value="${password}"/>
     * </dataSource>
     * </environment>
     * </environments>
     */
    private void environmentsElement(Element context) throws Exception, IllegalAccessException {
        String environment = context.attributeValue("default");
        List<Element> environmentList = context.elements("environment");
        for (Element e : environmentList) {
            String id = e.attributeValue("id");
            if(environment.equals(id)){
                //事物管理器
                TransactionFactory txFactory = (TransactionFactory)typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type"))
                    .newInstance();
                //数据源
                Element dataSourceElement = e.element("dataSource");
                DataSourceFactory dataSourceFactory = (DataSourceFactory)typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();
                List<Element> propertyList = dataSourceElement.elements("property");
                Properties prop = new Properties();
                for (Element property : propertyList) {
                    prop.setProperty(property.attributeValue("name"),property.attributeValue("value"));
                }
                
                dataSourceFactory.setProperties(prop);
                DataSource dataSource = dataSourceFactory.getDataSource();
                
                //构建环境
                Enviornment.Builder environmentBuilder = new Enviornment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                configuration.setEnviornment(environmentBuilder.build());
            }
        }
    }

    private void mapperElement(Element mappers) throws Exception {
        List<Element> mapperList = mappers.elements("mapper");
        for (Element element : mapperList) {
            String resource = element.attributeValue("resource");
            Reader reader = Resources.getResourceAsReader(resource);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(new InputSource(reader));
            Element root = document.getRootElement();
            //找命名空间
            String namespace = root.attributeValue("namespace");
            
            //select
            List<Element> selectNodes = root.elements("select");
            for (Element node : selectNodes) {
                String id = node.attributeValue("id");
                String parameterType = node.attributeValue("parameterType");
                String resultType = node.attributeValue("resultType");
                String sql = node.getText();
                Map<Integer,String> parameter = new HashMap<>();
                //匹配问号？
                Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                Matcher matcher = pattern.matcher(sql);
                for (int i = 1; matcher.find(); i++) {
                    String g1 = matcher.group(1);
                    String g2 = matcher.group(2);
                    parameter.put(i,g2);
                    sql = sql.replace(g1,"?");
                }

                String msId = namespace + "." + id;
                String nodeName = node.getName();
                SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

                BoundSql boundSql = new BoundSql(sql,parameterType,resultType,parameter);
                MappedStatement mappedStatement = new Builder(configuration, msId, sqlCommandType, 
                    boundSql).build();
                //添加解析sql
                configuration.addMappedStatement(mappedStatement);
            }
            //注册mapper映射机
            configuration.addMapper(Resources.classForName(namespace));
        }
    }
}
